﻿using System;
using System.Linq;
using System.Windows.Controls;
using Rio.Framework.Pages.Workflow.Models;
using NetBay.Core.BusinessEntities.Workflow;
using System.Collections.ObjectModel;
using Telerik.Windows.Controls.GridView;
using System.Windows;
using Telerik.Windows.Controls;
using Telerik.Windows.Data;
using System.Collections;
using Rio.Framework.Pages.Controls.Models;

namespace Rio.Framework.Pages.Workflow
{
    public partial class WorkflowStateCustomFilter : ControlBase, IFilteringControl
    {
        #region Attributs

        public static readonly DependencyProperty IsActiveProperty =
            DependencyProperty.Register(
            "IsActive",
            typeof(bool),
            typeof(WorkflowStateCustomFilter),
            new System.Windows.PropertyMetadata(false));

        private GridViewColumn dataFieldDescriptor;
        private Func<GridViewColumn, bool, IEnumerable> getDistinctValuesCallback;
        private Func<object, object> convertedAndFormattedValueFunc;
        private ObservableCollection<ISelectableItem<object>> _items = new ObservableCollection<ISelectableItem<object>>();
        private CompositeFilterDescriptor compositeFilter;
        private string _filterText = string.Empty;
        private string dataMemberName;
        private FilterDescriptorCollection targetFilters;
        private FilterOperator _filterOperator;
        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether the filtering is active.
        /// </summary>
        public bool IsActive
        {
            get { return (bool)GetValue(IsActiveProperty); }
            set { SetValue(IsActiveProperty, value); }
        }

        /// <summary>
        /// Gets the items.
        /// </summary>
        /// <value>The items.</value>
        public ObservableCollection<ISelectableItem<object>> Items
        {
            get
            {
                return _items;
            }
            set
            {
                _items = value;

                OnPropertyChanged("Items");
            }
        }

        /// <summary>
        /// Gets or sets the operator.
        /// </summary>
        /// <value>The operator.</value>
        public FilterOperator Operator
        {
            get
            {
                return _filterOperator;
            }
            set
            {
                _filterOperator = value;
                OnPropertyChanged("Operator");
            }
        }

        #endregion Properties

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="WorkflowStateCustomFilter" /> class.
        /// </summary>
        public WorkflowStateCustomFilter()
        {
            InitializeComponent();

            Operator = FilterOperator.IsEqualTo;
        }

        #endregion

        #region Functions

        /// <summary>
        /// Creates the filter expression.
        /// </summary>
        private void ApplyFilterExpression()
        {
            this.compositeFilter.FilterDescriptors.Clear();
            foreach (var item in Items)
            {
                if (item.IsSelected)
                {
                    //Create filter descriptor and add it to the descriptor collection
                    var filter = new FilterDescriptor(this.dataMemberName, Operator, item.Value)
                    {
                        MemberType = (this.dataFieldDescriptor as GridViewBoundColumnBase).DataType
                    };

                    this.compositeFilter.FilterDescriptors.Add(filter);
                }
            }
        }

        /// <summary>
        /// Creates the filters.
        /// </summary>
        private void CreateFilters()
        {
            this.compositeFilter = new CompositeFilterDescriptor
            {
                LogicalOperator = FilterCompositionLogicalOperator.Or
            };
            this.compositeFilter.FilterDescriptors.CollectionChanged += this.OnCompositeFilterDescriptorsCollectionChanged;
        }

        /// <summary>
        /// Called when [composite filter descriptors collection changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs" /> instance containing the event data.</param>
        private void OnCompositeFilterDescriptorsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (this.compositeFilter.FilterDescriptors.Count > 0)
            {
                if (!this.targetFilters.Contains(this.compositeFilter))
                {
                    this.targetFilters.Add(this.compositeFilter);
                }
            }
            else
            {
                this.targetFilters.Remove(this.compositeFilter);
            }

            this.OnPropertyChanged("IsActive");
        }

        /// <summary>
        /// Called when [clear].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs" /> instance containing the event data.</param>
        private void OnClear(object sender, RoutedEventArgs e)
        {
            this.CheckboxList.ClearSelection();
            this.compositeFilter.FilterDescriptors.Clear();
            this.IsActive = false;
        }

        /// <summary>
        /// Called when [filter].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs" /> instance containing the event data.</param>
        private void OnFilter(object sender, RoutedEventArgs e)
        {
            ApplyFilterExpression();
            this.IsActive = true;
        }

        /// <summary>
        /// Determines whether [is distinct value] [the specified text].
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns></returns>
        private bool IsDistinctValue(string text)
        {
            return this.Items.Where(dv => dv.Text.Equals(text)).Count() > 0;
        }

        /// <summary>
        /// Populates the distinct values.
        /// </summary>
        private void PopulateDistinctValues()
        {
            // Backup the values since we will repopulate the collection
            ObservableCollection<ISelectableItem<object>> OldItems = new ObservableCollection<ISelectableItem<object>>(Items);
            Items.Clear();

            foreach (object rawDistinctValue in this.getDistinctValuesCallback(this.dataFieldDescriptor, false))
            {
                bool isSelected = false;

                if (this.convertedAndFormattedValueFunc != null)
                {
                    // Convert the distinct value to take into account the
                    // IValueConverter on the column's DataMemberBinding (if any)
                    // and the column's DataFormatString (if any). If none of them
                    // exists, then this would be the identity function, i.e. it will
                    // return the same thing that was passed in.
                    object value = this.convertedAndFormattedValueFunc(rawDistinctValue);
                    var oldIsSelectedValue = OldItems.Where(p => p.Value == value).FirstOrDefault();
                    if (oldIsSelectedValue != null)
                    {
                        isSelected = oldIsSelectedValue.IsSelected;
                    }

                    Items.Add(new SelectableItemViewModel<object>(isSelected, rawDistinctValue.ToString(), this.convertedAndFormattedValueFunc(rawDistinctValue)));
                }
                else
                {
                    // Return the "raw" unformatted distinct value.
                    var oldIsSelectedValue = OldItems.Where(p => p.Value == rawDistinctValue).FirstOrDefault();
                    if (oldIsSelectedValue != null)
                    {
                        isSelected = oldIsSelectedValue.IsSelected;
                    }

                    Items.Add(new SelectableItemViewModel<object>(false, rawDistinctValue.ToString(), rawDistinctValue));
                }
            }
            OldItems = null;

        }

        /// <summary>
        /// Handles the ItemSelectionChanged event of the CheckboxList control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs" /> instance containing the event data.</param>
        private void CheckboxList_ItemSelectionChanged(object sender, EventArgs e)
        {
            ApplyFilterExpression();
        }

        /// <summary>
        /// Prepares the component for the column it will service.
        /// </summary>
        /// <param name="column">The column to prepare for.</param>
        public void Prepare(GridViewColumn column)
        {
            if (this.compositeFilter == null)
            {
                this.dataFieldDescriptor = column;
                this.getDistinctValuesCallback = column.DataControl.GetDistinctValues;
                this.targetFilters = column.DataControl.FilterDescriptors;
                this.dataMemberName = (this.dataFieldDescriptor as GridViewBoundColumnBase).GetDataMemberName();
                this.CreateFilters();
                this.convertedAndFormattedValueFunc = (this.dataFieldDescriptor as GridViewBoundColumnBase).CreateConvertedAndFormattedValueFunc();
            }

            PopulateDistinctValues();
        }

        #endregion

    }

}
